package nl.science.uva.pico.compiler.stacklanguage;

import static org.apache.commons.lang.StringUtils.isEmpty;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class takes care of mapping named source variable on the stack
 * 
 * @author Ivo van Dongen, Karel Pieterson
 *
 * @version 1.0
*/
public class VariableIndexMap {
	//enable logging
	private Logger logger = LoggerFactory.getLogger(VariableIndexMap.class);
	
	//define symbolic name for start of map (wrap otherwise magic number)
	private int currentIndex = 0;
	private Map<String, Integer> variableNameToIndexMap = new HashMap<String, Integer>();
	
	/**
	 * Adds a variable to the mapping. Automatically increments the static stack by one and shifts all earlier references
	 * @param name the variable name (must be unique)
	 */
	public void addVariable(String name) {
		if(isEmpty(name)) {
			throw new IllegalArgumentException("Variable name cannot be empty");
		}
		
		//Check whether the variable is not mapped yet
		if(variableNameToIndexMap.keySet().contains(name)) {
			throw new IllegalStateException("Variable already mapped: " + name);
		}

		//Add the variable
		variableNameToIndexMap.put(name, currentIndex);

		//Increase the stack size
		increaseStackSize();
		
		logger.debug("Mapped variable: " + name + " - " + this);
	}
	
	/**
	 * Retrieves the stack index for the given variable
	 * 
	 * @param name the name of the variable to get the index for
	 * @return the stack index for the given variable name
	 */
	public int getIndex(String name) {
		if(isEmpty(name)) {
			throw new IllegalArgumentException("Empty variable name");
		}
		
		Integer index = variableNameToIndexMap.get(name);
		
		//index should exist and be 0 or larger
		if(index == null || index < 0) {
			throw new IllegalStateException("Illegal value for variable: " + name + ": " + index);
		}

		logger.debug("Returning index for variable: " + name + " value: " + index);

		return index;
	}
	
	/**
	 * Increases the stack size and alters all variable references accordingly
	 */
	private void increaseStackSize() {
		currentIndex++;
		logger.debug("Increased stack size: " + this);
	}
	
	/**
	 * Prints the current stack
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return variableNameToIndexMap.toString();
	}
}
